package com.zatech.cgnci.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zatech.cgnci.project.base.enums.BillTypeEnum;
import com.zatech.cgnci.project.base.enums.ResponseCodeEnum;
import com.zatech.cgnci.project.base.exception.GlobalException;
import com.zatech.cgnci.project.base.utils.BeanUtils;
import com.zatech.cgnci.project.base.utils.ConvertUtils;
import com.zatech.cgnci.project.base.utils.DateUtils;
import com.zatech.cgnci.project.base.utils.VerifyUtils;
import com.zatech.cgnci.project.entity.*;
import com.zatech.cgnci.project.mapper.*;
import com.zatech.cgnci.project.service.CgnciReinsuranceInfoService;
import com.zatech.cgnci.project.service.CgnciRiskService;
import com.zatech.cgnci.project.service.CngciStatementPdfService;

import java.lang.reflect.Field;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;

import com.zatech.cgnci.project.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CngciStatementPdfServiceImpl implements CngciStatementPdfService {
    private final HttpServletResponse response;
    private final CgnciReinsurancePremiumBillMapper billMapper;
    private final CgnciProportionalLiabilityInvoiceMapper invoiceMapper;
    private final CgnciContractInvoiceMapper contractInvoiceMapper;
    private final CgnciContractBillDetailsMapper detailsMapper;
    private final CgnciProportionalLiabilityDetailsMapper LiabilityDetailsMapper;
    private final CgnciReinsurancePremiumDetailsMapper premiumDetailsMapper;
    private final CgnciReinsurancePaymentPlanMapper paymentPlanMapper;
    private final CgnciBusinessInsuranceMapper insuranceMapper;
    private final CgnciReinsuranceInfoService cgnciReinsuranceInfoService;
    private final CgnciRiskService riskService;
    private final CgnciRiskMapper riskMapper;
    private final static String CONTRACT_INVOICE = "合约账单.pdf";
    private final static String REINSURANCE_PREMIUM_BILL = "临分保费账单.pdf";
    private final static String PROPORTIONAL_LIABILITY_INVOICE = "临分摊赔账单.pdf";

    @Override
    @SneakyThrows
    public DownloadFileVO generatePdf(Long id, String billType) {
        DownloadFileVO vo = new DownloadFileVO();
        // 读取资源文件夹下的模板
        ClassPathResource resource = null;
        ContractInvoicePDFVO invoicePDFVO = new ContractInvoicePDFVO();
        ProportionalLiabilityInvoicePDFVO liabilityInvoicePDFVO = new ProportionalLiabilityInvoicePDFVO();
        PremiumBillPDFVO billPDFVO = new PremiumBillPDFVO();
        String pdf = null;
        Field[] fields = null;
        switch (BillTypeEnum.getEnumByCode(billType)) {
            case CONTRACT_INVOICE:
                resource = new ClassPathResource("file/ContractInvoice.pdf");
                CgnciContractInvoiceDO contractInvoiceDO = contractInvoiceMapper.selectById(id);
                CgnciBusinessInsuranceDO insuranceDO = insuranceMapper.selectOne(new LambdaQueryWrapper<CgnciBusinessInsuranceDO>()
                        .eq(CgnciBusinessInsuranceDO::getBusinessNo, contractInvoiceDO.getContractNumber())
                        .eq(CgnciBusinessInsuranceDO::getIsMirror, "0"));
                invoicePDFVO = insertInvoicePDFVO(contractInvoiceDO);
                if (Objects.nonNull(insuranceDO)) {
                    invoicePDFVO.setRisk(insuranceDO.getRiskSubType());
                }
                fields = invoicePDFVO.getClass().getDeclaredFields();
                pdf = CONTRACT_INVOICE;
                break;
            case PROPORTIONAL_LIABILITY_INVOICE:
                resource = new ClassPathResource("file/ProportionalLiabilityTwo.pdf");
                CgnciProportionalLiabilityInvoiceDO invoiceDO = invoiceMapper.selectById(id);
                liabilityInvoicePDFVO = insertLiabilityInvoicePDFVO(invoiceDO);
                fields = liabilityInvoicePDFVO.getClass().getDeclaredFields();
                pdf = PROPORTIONAL_LIABILITY_INVOICE;
                break;
            case REINSURANCE_PREMIUM_BILL:
                resource = new ClassPathResource("file/ReinsurancePremiumBill.pdf");
                CgnciReinsurancePremiumBillDO billDO = billMapper.selectById(id);
                billPDFVO = insertPremiumBill(billDO);
                fields = billPDFVO.getClass().getDeclaredFields();
                pdf = REINSURANCE_PREMIUM_BILL;
                break;
            default:
                throw new GlobalException(ResponseCodeEnum.HTTP_REQUEST_SERVICE_ERROR);
        }
        InputStream inputStream = resource.getInputStream();

        PdfReader reader = null;
        ByteArrayOutputStream bos = null;
        try {
            reader = new PdfReader(inputStream);
            bos = new ByteArrayOutputStream();
            PdfStamper pdfStamper = new PdfStamper(reader, bos);
            AcroFields acroFields = pdfStamper.getAcroFields();
            //设置宋体
            String rul = new ClassPathResource("file/xinsongti.ttf").getPath();
            BaseFont font = BaseFont.createFont(rul, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            // 中文字体
            //BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            float fontSize = 8f; // 字体大小
            for (Field field : fields) {
                field.setAccessible(true);
                // 设置文本域的字体为中文字体
                acroFields.setFieldProperty(field.getName(), "textfont", font, null);
                acroFields.setFieldProperty(field.getName(), "textsize", fontSize, null); // 设置字体大
                Object fieldValue = null;
                switch (BillTypeEnum.getEnumByCode(billType)) {
                    case CONTRACT_INVOICE:
                        // 将 map 中的值写到 pdf 模板对应的文本域中
                        fieldValue = field.get(invoicePDFVO);
                        break;
                    case PROPORTIONAL_LIABILITY_INVOICE:
                        // 将 map 中的值写到 pdf 模板对应的文本域中
                        fieldValue = field.get(liabilityInvoicePDFVO);
                        break;
                    case REINSURANCE_PREMIUM_BILL:
                        fieldValue = field.get(billPDFVO);
                        break;
                    default:
                        throw new GlobalException(ResponseCodeEnum.HTTP_REQUEST_SERVICE_ERROR);
                }
                String value = null;
                if (Objects.nonNull(fieldValue)) {
                    value = fieldValue.toString();
                }
                acroFields.setField(field.getName(), value);
            }
            // 如果为false那么生成的PDF文件还能编辑，所以一定要设为true
            pdfStamper.setFormFlattening(true);
            pdfStamper.close();
            // 返回文件
            vo.setPdfName(pdf);
            vo.setContent(bos.toByteArray());
            //ServletUtils.writeAttachment(response, pdf, bos.toByteArray());
        } catch (IOException | DocumentException | IllegalAccessException e) {
            throw new RuntimeException("生成 PDF 失败: " + e.getMessage(), e);
        } finally {
            try {
                assert bos != null;
                bos.close();
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return vo;
    }

    private PremiumBillPDFVO insertPremiumBill(CgnciReinsurancePremiumBillDO billDO) {
        PremiumBillPDFVO billPDFVO = ConvertUtils.convert(billDO, PremiumBillPDFVO.class);
        //查询保单信息
        LambdaQueryWrapper<CgnciBusinessInsuranceDO> query = new LambdaQueryWrapper<CgnciBusinessInsuranceDO>().eq(CgnciBusinessInsuranceDO::getBusinessNo, billDO.getPolicyNumber()).eq(CgnciBusinessInsuranceDO::getStatus, "3").eq(CgnciBusinessInsuranceDO::getIsEffective, "1").eq(CgnciBusinessInsuranceDO::getIsMirror, "0");
        CgnciBusinessInsuranceDO cgnciBusinessInsuranceDO = insuranceMapper.selectOne(query);
        if (Objects.isNull(cgnciBusinessInsuranceDO)) {
            return billPDFVO;
        }
        CgnciReinsuranceInfoVO infoVO = new CgnciReinsuranceInfoVO();
        infoVO = cgnciReinsuranceInfoService.getLfDivideOutReinsuranceByInsuranceId(cgnciBusinessInsuranceDO.getInsuranceId());
        if (CollectionUtils.isEmpty(infoVO.getRisks())) {
            infoVO = cgnciReinsuranceInfoService.getDivideInReinsuranceByInsuranceId(cgnciBusinessInsuranceDO.getInsuranceId());
        }
        Map<String, CgnciReinsuranceRiskVO> reinsuranceRiskVOMap = infoVO.getRisks().stream().collect(Collectors.toMap(
                CgnciReinsuranceRiskVO::getRiskSubTypeCode,
                paymentPlan -> paymentPlan
        ));
        String startDate = DateUtils.formatDate(billDO.getStartDate(), DateUtils.FORMAT_3);
        String endDate = DateUtils.formatDate(billDO.getEndDate(), DateUtils.FORMAT_3);
        billPDFVO.setPolicyTerm(startDate + " ~ " + endDate + " " + billPDFVO.getPolicyTerm() + "天");
        CgnciReinsuranceRiskVO riskVO = reinsuranceRiskVOMap.get(billDO.getPolicyType());
        //映射中文
        billPDFVO.setPolicyType(riskMapper.selectOne(new LambdaQueryWrapper<CgnciRiskDO>().eq(CgnciRiskDO::getRiskSubTypeCode, billDO.getPolicyType())).getRiskSubType());
        BigDecimal originalPremium = riskVO.getRiskInsureds().stream()
                .map(risk -> BeanUtils.sumBigDecimalProperty(risk.getRiskLayers(), CgnciReinsuranceRiskLayerVO::getGrossPremium))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        billPDFVO.setOriginalPremium(originalPremium);
        billPDFVO.setYourShare(VerifyUtils.isEmpty(billDO.getYourShare()) ? billDO.getYourShare() : billDO.getYourShare().multiply(new BigDecimal(100)));
        billPDFVO.setOriginalSumInsured(billDO.getMaxCompensation());
        billPDFVO.setProcedureRate(billDO.getProcedureRate().multiply(new BigDecimal(100)));
        List<CgnciReinsurancePremiumDetailsDO> detailsDOList = premiumDetailsMapper.selectList(new LambdaUpdateWrapper<CgnciReinsurancePremiumDetailsDO>()
                .eq(CgnciReinsurancePremiumDetailsDO::getInvoiceNumber, billDO.getInvoiceNumber()));
        Map<String, CgnciReinsurancePremiumDetailsDO> detailsDOMap = detailsDOList.stream().collect(Collectors.toMap(
                CgnciReinsurancePremiumDetailsDO::getFeeType,
                paymentPlan -> paymentPlan
        ));
        billPDFVO.setCommission(detailsDOMap.get("3").getAmount());
        billPDFVO.setNetPremium(detailsDOMap.get("2").getAmount());
        billPDFVO.setGrossPremium(billDO.getCollectAmount());

        List<CgnciReinsurancePaymentPlanDO> paymentPlanDOS = paymentPlanMapper.selectList(new LambdaUpdateWrapper<CgnciReinsurancePaymentPlanDO>()
                .eq(CgnciReinsurancePaymentPlanDO::getInvoiceNumber, billDO.getInvoiceNumber()));
        if (CollectionUtils.isEmpty(paymentPlanDOS)) {
            return billPDFVO;
        }
        billPDFVO.setPeriods(paymentPlanDOS.size());
        Map<Integer, CgnciReinsurancePaymentPlanDO> paymentPlanMap = paymentPlanDOS.stream()
                .collect(Collectors.toMap(
                        CgnciReinsurancePaymentPlanDO::getPaymentPeriod,
                        paymentPlan -> paymentPlan
                ));
        if (Objects.nonNull(paymentPlanMap.get(1))) {
            CgnciReinsurancePaymentPlanDO planDO1 = paymentPlanMap.get(1);
            billPDFVO.setPeriods_1("一期 " + planDO1.getPaymentDeadline() + " " + planDO1.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(2))) {
            CgnciReinsurancePaymentPlanDO planDO2 = paymentPlanMap.get(2);
            billPDFVO.setPeriods_2("二期 " + planDO2.getPaymentDeadline() + " " + planDO2.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(3))) {
            CgnciReinsurancePaymentPlanDO planDO3 = paymentPlanMap.get(3);
            billPDFVO.setPeriods_3("三期 " + planDO3.getPaymentDeadline() + " " + planDO3.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(4))) {
            CgnciReinsurancePaymentPlanDO planDO4 = paymentPlanMap.get(4);
            billPDFVO.setPeriods_4("四期 " + planDO4.getPaymentDeadline() + " " + planDO4.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(5))) {
            CgnciReinsurancePaymentPlanDO planDO5 = paymentPlanMap.get(5);
            billPDFVO.setPeriods_5("五期 " + planDO5.getPaymentDeadline() + " " + planDO5.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(6))) {
            CgnciReinsurancePaymentPlanDO planDO6 = paymentPlanMap.get(6);
            billPDFVO.setPeriods_6("六期 " + planDO6.getPaymentDeadline() + " " + planDO6.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(7))) {
            CgnciReinsurancePaymentPlanDO planDO7 = paymentPlanMap.get(7);
            billPDFVO.setPeriods_7("七期 " + planDO7.getPaymentDeadline() + " " + planDO7.getAmountPayable());
        }
        if (Objects.nonNull(paymentPlanMap.get(8))) {
            CgnciReinsurancePaymentPlanDO planDO8 = paymentPlanMap.get(8);
            billPDFVO.setPeriods_8("八期 " + planDO8.getPaymentDeadline() + " " + planDO8.getAmountPayable());
        }
        billPDFVO.setPeriods(paymentPlanDOS.size());

        return billPDFVO;
    }

    private ProportionalLiabilityInvoicePDFVO insertLiabilityInvoicePDFVO(CgnciProportionalLiabilityInvoiceDO invoiceDO) {
        ProportionalLiabilityInvoicePDFVO liabilityInvoicePDFVO = ConvertUtils.convert(invoiceDO, ProportionalLiabilityInvoicePDFVO.class);
        liabilityInvoicePDFVO.setLossDate(DateUtils.formatDate(invoiceDO.getLossDate(), DateUtils.FORMAT_3));
        liabilityInvoicePDFVO.setCurrentDate(DateUtils.formatDate(invoiceDO.getCreateTime(), DateUtils.FORMAT_3));
        liabilityInvoicePDFVO.setTotalAmount(VerifyUtils.isEmpty(invoiceDO.getTotalAmount()) ? null : invoiceDO.getCurrency() + " " + invoiceDO.getTotalAmount());
        liabilityInvoicePDFVO.setCededRatio(VerifyUtils.isEmpty(invoiceDO.getCededRatio()) ? null : invoiceDO.getCededRatio().multiply(BigDecimal.valueOf(100)) + "% of 100%");
        liabilityInvoicePDFVO.setCollectAmount(invoiceDO.getCurrency() + " " + invoiceDO.getCollectAmount());
        liabilityInvoicePDFVO.setRemarks(invoiceDO.getRemarks());
        liabilityInvoicePDFVO.setInsuredInsurer(invoiceDO.getInsuredInsurer());
        liabilityInvoicePDFVO.setAccountName("CGN Captive Insurance Limited");
        liabilityInvoicePDFVO.setAddress("Room 5505,55/F,Central Plaza,18 Harbour Road,Wan Chai,Hong Kong");
        liabilityInvoicePDFVO.setBankName("Bank of China(Hong Kong) Limited");
        liabilityInvoicePDFVO.setBankAddress("Bank of China Tower,1 Garden Road,Hong Kong");
        liabilityInvoicePDFVO.setSwiftCode("BKCHHKHHXXX");
        liabilityInvoicePDFVO.setBankCode("012");
        if ("HKD".equals(invoiceDO.getCurrency())) {
            liabilityInvoicePDFVO.setAccountNo("012-875-0-053394-6");
        } else {
            liabilityInvoicePDFVO.setAccountNo("012-875-92-70909-0");

        }
        liabilityInvoicePDFVO.setText1(invoiceDO.getCedingCompany());
        return liabilityInvoicePDFVO;
    }

    private ContractInvoicePDFVO insertInvoicePDFVO(CgnciContractInvoiceDO contractInvoiceDO) {
        ContractInvoicePDFVO invoicePDFVO = ConvertUtils.convert(contractInvoiceDO, ContractInvoicePDFVO.class);
        List<CgnciContractBillDetailsDO> detailsDOList = detailsMapper.selectList(new LambdaUpdateWrapper<CgnciContractBillDetailsDO>()
                .eq(CgnciContractBillDetailsDO::getInvoiceNumber, contractInvoiceDO.getInvoiceNumber()));
        for (CgnciContractBillDetailsDO ds : detailsDOList) {
            if ("1".equals(ds.getFeeType())) invoicePDFVO.setPremium(ds.getAmount());
            if ("2".equals(ds.getFeeType())) invoicePDFVO.setPremiumFree(ds.getAmount());
            if ("3".equals(ds.getFeeType())) invoicePDFVO.setCommission(ds.getAmount());
            if ("4".equals(ds.getFeeType())) invoicePDFVO.setPaidLoss(ds.getAmount());
            if ("5".equals(ds.getFeeType())) invoicePDFVO.setVat(ds.getAmount());
        }
        return invoicePDFVO;
    }
}
